package Download

import (
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

/**
http下载文件
*/
func HttpDownload(strUrl, strSavePath string, bMuilt bool, iIdIndex int64,
	scheduleCallback func(iIdIndex int64, schedule float64)) int {
	//下载客户端
	var httpClient = http.Client{Timeout: time.Second * 180}
	//线程池
	var threadGroup = sync.WaitGroup{}

	//创建临时文件
	var localFileSize int64
	var file *os.File
	defer file.Close()
	if fileInfo, fileErr := os.Stat(strSavePath); fileErr != nil {
		if os.IsNotExist(fileErr) {
			if createFile, err := os.Create(strSavePath); err == nil {
				file = createFile
			} else {
				return 8
			}
		} else {
			//创建临时文件失败
			return 9
		}
	} else {
		//文件已存在
		localFileSize = fileInfo.Size()
	}

	request, requestErr := http.NewRequest("HEAD", strUrl, nil)
	if requestErr == nil {
		response, responseErr := httpClient.Do(request)
		if responseErr == nil {
			defer response.Body.Close()
			//得到文件大小
			newFileSize := response.ContentLength
			if localFileSize == newFileSize {
				//文件已存在
				return 2
			} else {
				//判断是否支持多线下载
				if strings.Compare(response.Header.Get("Accept-Ranges"), "bytes") == 0 {
					if bMuilt == true {
						//支持 走下载流程
						if dispSliceDownload(httpClient, &threadGroup, file, newFileSize, strUrl,
							iIdIndex, scheduleCallback) != 0 {
							//下载异常
							return 999
						}
					} else {
						if clientDownload(httpClient, file, newFileSize, strUrl, iIdIndex, scheduleCallback) != 0 {
							return 998
						}
					}
				} else {
					//不支持多线程下载
					if clientDownload(httpClient, file, newFileSize, strUrl, iIdIndex, scheduleCallback) != 0 {
						return 998
					}
				}
			}
		} else {
			//返回错误
			return 4
		}
	} else {
		//请求错误
		return 5
	}
	return 0
}

/**
切片并下载
*/
func dispSliceDownload(httpClient http.Client, threadGroup *sync.WaitGroup, file *os.File, fileSize int64,
	strUrl string, iIdIndex int64, scheduleCallback func(iIdIndex int64, schedule float64)) int {

	var packageSize int64 = 1048576 * 4
	//文件总大小除以 每个线程下载的大小
	iNum := fileSize / packageSize
	//保证文件下载完整
	if fileSize%packageSize > 0 {
		iNum += 1
	}
	//下载总进度
	var schedule int64
	//分配下载线程
	for iCount := 0; iCount < int(iNum); iCount++ {
		//计算每个线程下载的区间,起始位置
		var iStartPosition int64
		var iEndPosition int64
		iStartPosition = int64(int64(iCount) * packageSize)
		iEndPosition = iStartPosition + packageSize
		if int64(iEndPosition) > fileSize {
			iEndPosition = iEndPosition - (iEndPosition - fileSize)
		}
		//构建请求
		if request, requestErr := http.NewRequest("GET", strUrl, nil); requestErr == nil {
			request.Header.Set(
				"Range",
				"bytes="+strconv.FormatInt(iStartPosition, 10)+"-"+strconv.FormatInt(iEndPosition, 10))
			//
			threadGroup.Add(1)
			go sliceDownload(httpClient, threadGroup, request, file, &fileSize, &schedule, iStartPosition,
				iIdIndex, scheduleCallback)
		} else {
			//请求失败
			return 999
		}

	}
	//等待所有线程完成下载
	threadGroup.Wait()
	return 0
}

/**
下载切片
*/
func sliceDownload(httpClient http.Client, threadGroup *sync.WaitGroup, request *http.Request, curFile *os.File,
	fileSize *int64, schedule *int64, iStartPosition int64, iIdeindex int64,
	scheduleCallback func(iIdIndex int64, schedule float64)) int {
	defer threadGroup.Done()
	response, responseErr := httpClient.Do(request)
	if responseErr == nil && response.StatusCode == 206 {
		defer response.Body.Close()
		bytes, err := ioutil.ReadAll(response.Body)
		if err == nil {
			iByteLen := len(bytes)
			//从我们计算好的起点写入文件
			curFile.WriteAt(bytes, iStartPosition)
			//暂时注释掉进度
			atomic.AddInt64(schedule, int64(iByteLen))
			val := atomic.LoadInt64(schedule)
			//文件下载总进度
			fPercent := float64(val*1.0) / float64(*fileSize) * 100
			scheduleCallback(iIdeindex, float64(fPercent))
		} else {
			//读取流失败
			return 8
			//panic(err)
		}
	} else {
		//获取数据失败
		return 9
		//panic(responseErr)
	}
	return 0
}

/**
单文件下载
*/
func clientDownload(httpClient http.Client, file *os.File, fileSize int64, strUrl string,
	iIdIndex int64, scheduleCallback func(iIdIndex int64, schedule float64)) int {
	var (
		onePackage = make([]byte, 32*1024)
		iHasWrite  int64
	)
	defer file.Close()
	if request, requestErr := http.NewRequest("GET", strUrl, nil); requestErr == nil {
		response, responseErr := httpClient.Do(request)
		if responseErr == nil && response.StatusCode == 200 {
			defer response.Body.Close()
			for {
				//读取bytes
				if iRead, packageErr := response.Body.Read(onePackage); packageErr == nil && iRead > 0 {
					if iWrite, writeErr := file.Write(onePackage[0:iRead]); writeErr == nil && iWrite > 0 {
						iHasWrite += int64(iWrite)
						fPercent := float64(iHasWrite*1.0) / float64(fileSize) * 100
						scheduleCallback(iIdIndex, float64(fPercent))
					} else {
						//写入失败
						return 7
					}
				} else {
					//读取流失败
					return 8
				}
			}
		} else {
			return 9
		}
	} else {
		//请求失败
		return 999
	}
	return 0
}
